<?php
namespace DRY\Module;

/**
 * DonReY Framework 2012 :: Serializer module - Uses various algorithms ( some built into PHP, others through external libraries, others by using it's own sub-modules ),
 *   to transform arbitrary PHP data into a string, and from that string back into PHP data. This is an extension to PHP's serialize() and unserialize()
 *
 * @author Wavetrex <wavetrex@gmail.com>
 * @link http://www.donrey.net/Documentation/Structure/Modules/serializer.html
 * @license [GPL v3] http://www.gnu.org/licenses/gpl-3.0.html
 * @version 1.0.dev
 * @package DonReY
 * @subpackage Module.Serializer
 */
class Serializer extends base
{
	// Exception/Error codes
	const
		EXC_NO_ALGORITHM_DEFINED = 1,
		EXC_UNSUPPORTED_ALGORITHM = 2
		;

	const
		Autoload_Directory = 'serialize',
		Autoload_Prefix = 'DRY\Serialize\Algorithm',
		Autoload_Suffix = 'algorithm'
		;

	/**
	 * Converts from PHP data into a string - Uses the algorithm defined in the Config, or php serialize() by default
	 * @param mixed $value
	 * @return string
	 */
	public function serialize($value, $extra = null)
	{
		if(!isset($this-> CFG-> algorithm))
			throw new \DRY\Water('serializer', self::EXC_NO_ALGORITHM_DEFINED);

		// TODO: For all, check the return value (depending on algorithm) and return bool(false) if serialization failed
		switch($this-> CFG-> algorithm) {

			case 'serialize':
				return serialize($value);

			case 'igbinary':
				return \igbinary_serialize($value);

			case 'php':
				return 'return '.var_export($value, true).';';

			case 'json':
				return json_encode($value, $extra);

			case 'yaml':
				return \Spyc::YAMLDump($value);

			default:
				$algo_class = self::Autoload_Prefix.'_'.$this-> CFG-> algorithm;
				try {
					return $algo_class::serialize($value, $extra);
				} catch (\Exception $e) {
					throw new \DRY\Water('serializer', self::EXC_UNSUPPORTED_ALGORITHM, array('algo'=>(string)$this-> CFG-> algorithm));
				}
		}
	}

	/**
	 * Converts from a string into PHP data - Uses the algorithm defined in the Config, or php unserialize() by default
	 * @param string $str
	 * @return mixed
	 */
	public function unserialize($str, $extra = null)
	{
		if(!isset($this-> CFG-> algorithm))
			throw new \DRY\Water('serializer', self::EXC_NO_ALGORITHM_DEFINED);

		// TODO: For all, check the return value (depending on algorithm) and return NULL if unserialization failed
		switch($this-> CFG-> algorithm) {

			case 'serialize':
				return unserialize($str);

			case 'igbinary':
				return \igbinary_unserialize($str);

			case 'php':	// not safe ! DO NOT USE unless really-really necessary, and never on User's data
				return eval($str);

			case 'json':
				return json_decode($str, true, $extra);

			case 'yaml':
				return \Spyc::YAMLLoadString($str);

			default:
				$algo_class = self::Autoload_Prefix.'_'.$this-> CFG-> algorithm;
				try {
					return $algo_class::unserialize($str, $extra);
				} catch (\Exception $e) {
					throw new \DRY\Water('serializer', self::EXC_UNSUPPORTED_ALGORITHM, array('algo'=>(string)$this-> CFG-> algorithm));
				}
		}
	}
}

namespace DRY\Serializer;

interface Algorithm_base
{
	static public function serialize($data);

	static public function unserialize(&$str);
}